🔥【Vue.js → Nuxt 入門推薦!🌟 新書即將上市 🌟】
📘《想要 SSR 嗎?就使用 Nuxt 吧!Nuxt 讓 Vue.js 更好處理 SEO 搜尋引擎最佳化》
👀 Nuxt v4 內容與範例也可以參考並購買本系列文筆著所著書籍
📦 預計於:2025/08/14 出版,目前天瓏書局預購有 7️⃣8️⃣ 折優惠
👉 點此前往購買:https://pse.is/7yulm5
注意:Nuxt 4 已於 2025/07/16 釋出,本文部分內容或範例可能和最新版本有所不同
Nuxt 3 提供了一個布局模板 (Layouts) 的功能,可以讓你定義好布局模板後,在整個 Nuxt 中使用,舉例來說就很適合如上方有導覽列,下方是網頁主體內容的這種排版方式,將其構建成一個布局模板後,我們就可以重複使用這種布局方式。
布局模板通常放置在 ./layouts 目錄之下,也具有異步自動導入的效果,當新增好布局檔案後,我們就可以在 app.vue 中,添加 <NuxtLayout /> 元件來表示使用布局模板,也可以通過 name 設定不同的模板名稱。
布局模板在 Nuxt 3 中有約定一個名稱為 default.vue 作為預設的模板,如果在頁面元件中未特別指定要使用哪個模板或 <NuxtLayout /> 沒有設定 name 屬性,那麼都將會使用 default 作為預設的布局。
新增 ./layouts/default.vue 檔案內容如下:
<template>
  <div class="bg-sky-100 py-2">
    <p class="px-6 py-4 text-2xl text-gray-700">這是預設的布局,全部頁面都會使用到</p>
    <slot />
  </div>
</template>
在布局模板中,通常會包含一個 <slot /> 插槽,這個未命名的插槽 (slot) 即為預設插槽,這將會是採用這個布局模板的頁面元件,顯示的內容容器位置。
調整 app.vue 檔案,內容如下,我們添加 <NuxtLayout> 作為布局模板顯示的位置,name 屬性預設是 default,不過我們還是寫上 name="default" 避免誤會,這個 name 屬性值對應的即是布局模板的名稱。
下圖可以看見,我們在 app.vue 所寫的文字「這裡是最外層 app.vue」,會是在最外層,而緊接著的 <NuxtLayout name="default"></NuxtLayout>,就是布局頁面 default.vue。

如果你有注意到,default.vue 檔案內程式碼內,有一個插槽 <slot />,這裡就會是 <NuxtLayout> 內的元素所顯示的位置。
例如,我們在 app.vue 稍作調整:
<template>
  <div class="m-4 bg-white">
    <p class="pb-4 text-2xl text-slate-600">這裡是最外層 app.vue</p>
    <NuxtLayout name="default">
      <p class="px-6 pt-4 text-xl text-slate-800">
        被 NuxtLayout 包裹的元件將會放置到 Layout 的 slot 中
      </p>
    </NuxtLayout>
  </div>
</template>
被 <NuxtLayout name="default"> 包裹的元素,就會在布局模板中的插槽 <slot /> 顯示。
當然,你也可以在布局模板中添加多個插槽,並給予名稱,這樣就可以將內容安排到特定的位置。
header 與 footer如果插槽沒有給予
name屬性,預設為default。./layouts/default.vue檔案內容如下:
<template>
  <div class="bg-sky-100 py-2">
    <p class="px-6 py-4 text-2xl text-gray-700">這是預設的布局,全部頁面都會使用到</p>
    <slot name="header" />
    <slot />
    <slot name="footer" />
  </div>
</template>
app.vue,內容如下:
<template>
  <div class="m-4 bg-white">
    <p class="pb-4 text-2xl text-slate-600">這裡是最外層 app.vue</p>
    <NuxtLayout name="default">
      <template #header>
        <p class="px-6 pt-4 text-xl text-green-500">這段會放置在 header 插槽</p>
      </template>
      <template #default>
        <p class="px-6 pt-4 text-xl text-cyan-500">
          被 NuxtLayout 包裹的元件將會放置到 Layout 的預設 slot 中
        </p>
      </template>
      <template #footer>
        <p class="px-6 pt-4 text-xl text-blue-500">這段會放置在 footer 插槽</p>
      </template>
    </NuxtLayout>
  </div>
</template>
下圖可以看見,我們就可以依此來安排各個元件,於布局模板插槽的所在位置。
當你熟悉了插槽配置,你也可以在其中添加 <NuxtPage /> 與建立 pages 下的頁面元件,以達到不同的路由頁面,使用相同的布局方式。
如果布局模板結合了路由頁面,整體網站就會如下的巢狀顯示方式,網站的入口點 app.vue 放置布局模板,模板內的內容則使用路由的 <NuxtPage />,最後各個路由的頁面就會在 <NuxtPage /> 容器中顯示。
+---------------------------+
| app.vue                   |
| +-----------------------+ |
| | layout                | |
| | +-------------------+ | |
| | | page              | | |
| | |                   | | |
| | |                   | | |
| | +-------------------+ | |
| +-----------------------+ |
+---------------------------+
app.vue 入口點將 app.vue 調整為以下內容:
<template>
  <div class="m-4 bg-white">
    <p class="pb-4 text-2xl text-slate-600">這裡是最外層 app.vue</p>
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </div>
</template>
建立 ./pages/index.vue,內容如下:
<template>
  <div class="m-6 bg-slate-50 py-24">
    <div class="flex flex-col items-center">
      <h1 class="text-6xl font-semibold text-sky-400">2022 iThome</h1>
      <p class="mt-4 text-9xl font-bold text-gray-600">鐵人賽</p>
    </div>
  </div>
</template>
這樣我們就可以來讓路由頁面共用相同的布局模板。
承上,我們建立好預設的布局模板,讓它負責顯示路由的頁面。
新增 ./pages/about.vue,內容如下:
<template>
  <div class="mx-6 mb-4 bg-slate-50 py-24">
    <div class="flex flex-col items-center">
      <h1 class="text-6xl font-semibold text-yellow-400">大家好!我是 Ryan</h1>
      <p class="my-8 text-3xl text-gray-600">這裡是 /about</p>
    </div>
  </div>
</template>
新增 ./pages/contact.vue,內容如下:
<template>
  <div class="mx-6 mb-4 bg-slate-50 py-24">
    <div class="flex flex-col items-center">
      <h1 class="text-6xl font-semibold text-rose-400">如果沒事不要找我 xDDD</h1>
      <p class="my-8 text-3xl text-gray-600">這裡是 /contact</p>
    </div>
  </div>
</template>
調整 ./pages/index.vue,內容如下:
<template>
  <div class="mx-6 mb-4 bg-white py-24">
    <div class="flex flex-col items-center">
      <h1 class="text-6xl font-semibold text-gray-800">這裡是首頁</h1>
      <div class="my-4 flex space-x-4">
        <NuxtLink to="/about">前往 About</NuxtLink>
        <NuxtLink to="/contact">前往 Contact</NuxtLink>
      </div>
    </div>
  </div>
</template>
可以發現,現在首頁 /、/about 與 /contact 都套用上了預設布局。
你也可以建立多個布局模板,再依據不同的情境,使用不同的布局模板。
新增 ./layouts/custom.vue,內容如下:
<template>
  <div class="bg-rose-100 py-2">
    <p class="px-6 py-4 text-2xl text-gray-700">
      使用 <span class="font-bold text-rose-500">Custom</span> 布局
    </p>
    <slot />
  </div>
</template>
新增 ./pages/custom.vue,內容如下:
<template>
  <div class="m-6 bg-slate-50 py-24">
    <div class="flex flex-col items-center">
      <h1 class="text-6xl font-semibold text-sky-400">2022 iThome</h1>
      <p class="mt-4 text-9xl font-bold text-gray-600">鐵人賽</p>
    </div>
  </div>
</template>
在 ./pages/custom.vue 中的 script 使用 definePageMeta 方法:
<script setup>
definePageMeta({
  layout: 'custom'
})
</script>
definePageMeta 方法,提供我們可以設定特定的布局模板,layout 參數值所對應的名稱,即為 ./layouts 目錄下的布局模板。
注意,布局模板的命名被規範使用
Kebab Case命名法,若檔案名稱為 customLayout.vue,它將會以custom-layout作為name屬性傳遞給

我們能使用 layout: false 來禁止使用預設的布局模板,並在 template 添加 <NuxtLayout  name="custom"> 來使用 custom 布局模板。
<template>
  <NuxtLayout name="custom">
    <div class="mx-6 mb-4 bg-white py-24">
      <div class="flex flex-col items-center">
        <h1 class="text-6xl font-semibold text-sky-400">2022 iThome</h1>
        <p class="mt-4 text-9xl font-bold text-gray-600">鐵人賽</p>
      </div>
    </div>
  </NuxtLayout>
</template>
<script step>
definePageMeta({
  layout: false
})
</script>
當布局模板可以在 template 中設定使用,我們就能結合插槽甚至動態的調整 name 屬性,做出更多樣靈活的布局效果。
上圖的參考程式碼:./layouts/custom.vue
<template>
  <div class="bg-rose-100 py-2">
    <p class="px-6 py-4 text-2xl text-gray-700">
      使用 <span class="font-bold text-rose-500">Custom</span> 布局
    </p>
    <slot />
    <slot name="footer" />
  </div>
</template>
./pages/custom.vue
<template>
  <NuxtLayout name="custom">
    <template #default>
      <div class="mx-6 mb-4 bg-white py-24">
        <div class="flex flex-col items-center">
          <h1 class="text-6xl font-semibold text-sky-400">2022 iThome</h1>
          <p class="mt-4 text-9xl font-bold text-gray-600">鐵人賽</p>
        </div>
      </div>
    </template>
    <template #footer>
      <div class="flex flex-col items-center">
        <p class="mt-4 text-xl text-slate-600">感謝您閱讀 Nuxe 3 學習筆記~</p>
      </div>
    </template>
  </NuxtLayout>
</template>
<script step>
definePageMeta({
  layout: false
})
</script>
當你使用 definePageMeta 方法,禁止使用預設的布局模板後,你也能使用 setPageLayout 方法來動態改變布局。
例如:
<script setup>
const enableCustomLayout = () => {
  setPageLayout('custom')
}
</script>
我們將這篇所介紹的布局模板與路由頁面的配置方式與特性熟悉之後,我們就來做一個小練習。
使用預設布局模板,使得每一個路由頁面都能有 Header,點擊「Nuxt 3 學習筆記」隨時可以回至首頁,並將其中一個頁面禁止使用預設布局,改採用自訂的布局模板。
當你看過了這篇內容後,你會發現 Nuxt 3 所提供的布局模板非常的好用,布局模板規劃好後,爾後頁面所使用到的相同布局,只需要更改同一個布局模板,如果再結合元件化技巧,更是讓你的程式碼兼具重複使用性與維護性。
感謝大家的閱讀,這是我第一次參加 iThome 鐵人賽,請鞭小力一些,也歡迎大家給予建議 :)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。
您好,文章中提到的這段程式碼是很像有誤...
<script setup>
// 錯誤的版本
const = enableCustomLayout () => {
  setPageLayout('custom')
}
// 修正後
const enableCustomLayout = () => {
  setPageLayout('custom')
}
</script>
您好,應該是撰寫時輸入錯誤
感謝提醒,已經做修正了